test(recorded): add client cassette coverage (3/5)#1976
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
6fec9aa to
9cad57c
Compare
dc3a4a7 to
8000a8e
Compare
Greptile SummaryThis PR adds client-level cassette coverage for OpenAI chat and embeddings, forming part 3 of a 5-PR stack that decomposes recorded end-to-end replay coverage into reviewable slices. All cassettes are correctly sanitized (no secrets, volatile fields normalized) and the VCR fixture plumbing — cassette directory, body matcher, serializer — aligns with the harness established in earlier PRs.
|
| Filename | Overview |
|---|---|
| tests/recorded/clients/init.py | Package init file with license header and docstring; no functional code. |
| tests/recorded/clients/test_openai_chat.py | Async VCR test for OpenAI chat; constructs OpenAICompatibleClient with an externally-managed httpx client, calls generate_async, and validates result shape against the cassette response. |
| tests/recorded/clients/test_openai_embeddings.py | Synchronous VCR test for OpenAI embeddings; directly instantiates OpenAIEmbeddingModel and validates that the decoded embedding vector has the expected dimension (1536). |
| tests/recorded/clients/cassettes/test_openai_chat/test_openai_chat_generate_text.yaml | Cassette for the chat test; volatile fields (response id, system_fingerprint) are correctly normalized and no secrets are present. |
| tests/recorded/clients/cassettes/test_openai_embeddings/test_openai_embeddings_sync.yaml | Cassette for the embeddings test; stores a single base64-encoded 1536-dim embedding; Authorization header is absent as expected after sanitization. |
| tests/recorded/clients/configs/openai_chat_config/config.yml | Minimal passthrough chat config referencing gpt-4o-mini; added in this PR but not yet loaded by any test in this slice of the stack. |
| tests/recorded/clients/configs/openai_embeddings_config/config.yml | Minimal embeddings config for text-embedding-3-small; added in this PR but not yet loaded by any test in this slice of the stack. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Test as Test (pytest)
participant VCR as VCR Cassette
participant Client as OpenAICompatibleClient / OpenAIEmbeddingModel
participant API as OpenAI API (mocked)
Note over Test,API: Replay mode (--block-network)
Test->>VCR: load cassette YAML
VCR-->>Test: cassette ready
alt Chat test (async)
Test->>Client: OpenAICompatibleClient(base_url, api_key, http_client)
Test->>Client: model.generate_async("Say hello in one word")
Client->>VCR: POST /v1/chat/completions
VCR-->>Client: "200 { content: "Hello!" }"
Client-->>Test: GenerationResult
Test->>Test: assert content, finish_reason, request_id, usage
else Embeddings test (sync)
Test->>Client: OpenAIEmbeddingModel("text-embedding-3-small", api_key)
Test->>Client: model.encode(["test"])
Client->>VCR: POST /v1/embeddings
VCR-->>Client: "200 { embedding: base64(...) }"
Client-->>Test: List[List[float]]
Test->>Test: "assert len==1, len(result[0])==1536"
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Test as Test (pytest)
participant VCR as VCR Cassette
participant Client as OpenAICompatibleClient / OpenAIEmbeddingModel
participant API as OpenAI API (mocked)
Note over Test,API: Replay mode (--block-network)
Test->>VCR: load cassette YAML
VCR-->>Test: cassette ready
alt Chat test (async)
Test->>Client: OpenAICompatibleClient(base_url, api_key, http_client)
Test->>Client: model.generate_async("Say hello in one word")
Client->>VCR: POST /v1/chat/completions
VCR-->>Client: "200 { content: "Hello!" }"
Client-->>Test: GenerationResult
Test->>Test: assert content, finish_reason, request_id, usage
else Embeddings test (sync)
Test->>Client: OpenAIEmbeddingModel("text-embedding-3-small", api_key)
Test->>Client: model.encode(["test"])
Client->>VCR: POST /v1/embeddings
VCR-->>Client: "200 { embedding: base64(...) }"
Client-->>Test: List[List[float]]
Test->>Test: "assert len==1, len(result[0])==1536"
end
Reviews (11): Last reviewed commit: "test(recorded): add client cassette cove..." | Re-trigger Greptile
9cad57c to
7310fa7
Compare
8000a8e to
a253ae2
Compare
7310fa7 to
6b783f2
Compare
a253ae2 to
3d1d7ea
Compare
6b783f2 to
e1954d4
Compare
5153569 to
caafb61
Compare
7000fcc to
5571204
Compare
caafb61 to
3136aa1
Compare
5571204 to
fa2ce24
Compare
3136aa1 to
06858a2
Compare
fa2ce24 to
05def3f
Compare
06858a2 to
474d54d
Compare
05def3f to
f912ae5
Compare
474d54d to
36679f9
Compare
f912ae5 to
e5a52c6
Compare
36679f9 to
d052d99
Compare
d052d99 to
3732314
Compare
e5a52c6 to
14ddbc7
Compare
tgasser-nv
left a comment
There was a problem hiding this comment.
Looks good! Just a few cleanups needed before merging.
Not blocking for this PR since it's the initial implementation, but can you add the following in a follow-on PR (can be outside this stack) to round out the coverage:
- Streaming inference
- Tool-calling
Foundation for converging the recorded suite's cross-surface drift, consumed by the public_api and library layers above: - rails/helpers.py: shared build_rails() construction helper + async_chunks() (replaces the LLMRails(load_config(...)) boilerplate inlined per test, D11/F). - assertions.py: assert_blocked_generation() asserts refusal + rail stop semantics, not just non-empty text (D6).
Replay under --block-network must not depend on ambient proxy env: a SOCKS proxy makes httpx raise ImportError (missing socksio) on a cassette hit, turning a deterministic replay into a shell-dependent error. Add an autouse fixture that strips proxy vars during replay (record_mode == none) while leaving them intact for recording. Also fix the README 'Adding a test' snippet to include the imports it relies on (LLMRails, load_config, suite-local snapshot, OPENAI_BASELINE_CONFIG) so a new contributor can copy-paste it and land on the intended snapshot re-export.
14ddbc7 to
82429ff
Compare
Adds the library-traversal sibling of test_load_prompts_sorts_files_for_deterministic_overrides, addressing the stack-2 review ask. Mocks os.walk to yield two library .co files defining the same bot message in non-sorted order and asserts the alphabetically-first file wins the collision, pinning the dirs.sort()/sorted(files) fix in LLMRails.__init__ so library load order stays filesystem-independent.
3732314 to
0c42208
Compare
|
Staged Fern docs preview: https://nvidia-preview-pr-1976.docs.buildwithfern.com/nemo/guardrails |
Summary
Adds recorded client-level coverage for OpenAI chat and embeddings flows.
Why
Client cassette smoke tests validate that the harness can replay basic provider traffic before the rails API coverage layers on top.
What Changed
Stack Position
Part 3 of 5.
Stack Context
This stack decomposes recorded end-to-end replay coverage into reviewable slices. The PRs should be reviewed against their parent branch in the stack.
Please review each PR against its parent branch, not directly against the root base branch, except for part 1.
stack/recorded-tests-01-harnessdevelopstack/recorded-tests-02-deterministic-library-loadstack/recorded-tests-01-harnessstack/recorded-tests-03-clientsstack/recorded-tests-02-deterministic-library-loadstack/recorded-tests-04-public-apistack/recorded-tests-03-clientsstack/recorded-tests-05-library-railsstack/recorded-tests-04-public-apiValidation